package com.aliang.shopping.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.aliang.shopping.config.OssConfig;
import com.aliang.shopping.model.converter.ChatConverter;
import com.aliang.shopping.model.enums.MessageStatusEnum;
import com.aliang.shopping.model.po.Message;
import com.aliang.shopping.model.vo.chat.ContactVO;
import com.aliang.shopping.model.vo.chat.MessageVO;
import com.aliang.shopping.service.ContactService;
import com.aliang.shopping.service.MessageService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.ConcurrentHashMap;

@Component
@Slf4j
@ServerEndpoint("/websocket/{userId}")
public class WebSocket {
    @Autowired
    private MessageService messageService;
    @Autowired
    private ContactService contactService;
    @Autowired
    private ChatConverter chatConverter;

    @Resource
    ObjectMapper objectMapper;
    @Resource
    private OssConfig ossConfig;

    private static WebSocket socket;

    @PostConstruct
    public void init() {
        socket = this;
        socket.messageService = this.messageService;
        socket.contactService = this.contactService;
        socket.chatConverter = this.chatConverter;
        socket.objectMapper = this.objectMapper;
        socket.ossConfig = this.ossConfig;
    }

    //与某个客户端的连接会话，需要通过它来给客户端发送数据
    private Session session;
    /**
     * 用户ID
     */
    private Long userId;


    // 用来存在线连接用户信息
    private static final ConcurrentHashMap<Long, Session> sessionPool = new ConcurrentHashMap<>();


    /**
     * 链接成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "userId") Long userId) {
        try {
            this.session = session;
            this.userId = userId;
            sessionPool.put(userId, session);
            log.info("【websocket消息】有新的连接，总数为:" + sessionPool.size());
        } catch (Exception e) {
            log.error("连接异常，异常信息：{}", e.getMessage());
        }
    }

    /**
     * 链接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        try {
            sessionPool.remove(this.userId);
            log.info("【websocket消息】连接断开，总数为:" + sessionPool.size());
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message
     */
    @OnMessage
    public void onMessage(String message) {
        if (message.contains("type")) {
            handleMessage(message);
        } else if (message.contains("unread")) {
            //更新联系人
            log.info("更新联系人,{}", message);
            handleContact(message);
        } else if (message.contains("heartBath")) {
            //心跳信息，直接返回
            sendOneMessage(userId, message);
        }
    }

    /**
     * 发送错误时的处理
     *
     * @param error
     */
    @OnError
    public void onError(Throwable error) {
        log.error("用户错误,原因:" + error.getMessage());
        error.printStackTrace();
    }


    /**
     * 处理消息
     *
     * @param message
     */
    private void handleMessage(String message) {
        MessageVO messageVO = JSONUtil.toBean(message, MessageVO.class);
        //定义结果，默认200，成功
        Dict set = Dict.create()
                .set("code", 200)
                .set("type", "message")
                .set("data", messageVO.getId());
        if (BeanUtil.hasNullField(messageVO)) {
            // 参数有问题，返回失败
            set.set("code", 201);
            sendOneMessage(userId, JSONUtil.toJsonStr(set));
            return;
        }
        // 保存消息
        try {
            //设置发送状态为成功
            messageVO.setStatus(MessageStatusEnum.succeed.name());
            //保存数据
            Message po = socket.chatConverter.toPO(messageVO);
            //如果是图片，需要截取图片
            if (StrUtil.equals(po.getType(), "image")) {
                po.setContent(StrUtil.subAfter(po.getContent(), socket.ossConfig.getUrl(), false));
            }
            socket.messageService.saveOrUpdate(po);
            //返回成功
            sendOneMessage(userId, JSONUtil.toJsonStr(set));
            //通知对方
            sendOneMessage(messageVO.getTo().getId(), socket.objectMapper.writeValueAsString(messageVO));
        } catch (Exception e) {
            log.error("保存消息异常，异常信息：{}", ExceptionUtil.getMessage(e));
            e.printStackTrace();
        }
    }


    /**
     * 处理联系人
     *
     * @param message
     */
    private void handleContact(String message) {
        ContactVO contactVO = JSONUtil.toBean(message, ContactVO.class);
        //定义结果，默认200，成功
        Dict set = Dict.create()
                .set("code", 200)
                .set("type", "contact")
                .set("data", String.valueOf(contactVO.getId()));
        //判断参数是否为空，忽略头像和最后一条消息
        if (BeanUtil.hasNullField(contactVO, "avatar", "lastContent")) {
            // 参数有问题，返回失败
            set.set("code", 201);
            sendOneMessage(userId, JSONUtil.toJsonStr(set));
            return;
        }
        //获取对方的session
        Session session = sessionPool.get(contactVO.getId());
        // 更新联系人
        socket.contactService.updateContactById(contactVO, userId, isOnline(session));
        // 返回成功
        sendOneMessage(userId, JSONUtil.toJsonStr(set));
    }


    // 此为广播消息
    public void sendAllMessage(String message) {
        log.info("【websocket消息】广播消息:" + message);
        for (Session session : sessionPool.values()) {
            try {
                if (isOnline(session)) {
                    session.getAsyncRemote().sendText(message);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    // 此为单点消息
    public void sendOneMessage(Long userId, String message) {
        Session session = sessionPool.get(userId);
        if (isOnline(session)) {
            try {
                if (!message.contains("heartBath"))
                    log.info("【websocket消息】 单点消息:" + message);
                session.getAsyncRemote().sendText(message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    // 此为单点消息(多人)
    public void sendMoreMessage(Long[] userIds, String message) {
        for (Long userId : userIds) {
            Session session = sessionPool.get(userId);
            if (isOnline(session)) {
                try {
                    log.info("【websocket消息】 单点消息:" + message);
                    session.getAsyncRemote().sendText(message);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

    }

    /**
     * 判断是否在线
     *
     * @param session
     * @return
     */
    private boolean isOnline(Session session) {
        return session != null && session.isOpen();
    }

}